/*
 * Copyright (C) 2011 Johan Maasing johan at zoom.nu Licensed under the Apache
 * License, Version 2.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
 * or agreed to in writing, software distributed under the License is
 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package nu.zoom.catonine.tail;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import nu.zoom.catonine.tail.TailerListener.TailerLinesEvent;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Parent class for tailers that notifies listeners of new log blocks.
 * 
 * @author "Johan Maasing" &lt;johan@zoom.nu&gt;
 */
public abstract class AbstractExecutorTailer implements Tailer {

	private final Log log = LogFactory.getLog(getClass());
	private final ArrayList<TailerListener> listeners = new ArrayList<TailerListener>();
	protected final ExecutorService executor = Executors.newCachedThreadPool();

	@Override
	public synchronized void addListener(final TailerListener listener) {
		this.listeners.add(listener);
	}

	@Override
	public synchronized void removeListener(final TailerListener listener) {
		this.listeners.remove(listener);
	}

	private synchronized List<TailerListener> copyListenerList() {
		ArrayList<TailerListener> copy = new ArrayList<TailerListener>(this.listeners);
		return Collections.unmodifiableList(copy);
	}

	/**
	 * Informs listeners of new log blocks that have been discovered.
	 * 
	 * @param lines
	 *            The new log blocks, may not be null.
	 */
	protected void fireLinesRead(List<TailerListener.LogEntry> lines) {
		log.trace("Informing listeners that we have read new lines");
		final List<TailerListener.LogEntry> readonlyList = Collections.unmodifiableList(lines);
		final List<TailerListener> listeners = copyListenerList();
		TailerLinesEvent event = new TailerLinesEvent();
		event.source = this;
		event.entries = readonlyList;
		for (final TailerListener listener : listeners) {
			listener.lines(event);
		}
	}

	/**
	 * Informs listeners that the tailer is working or has become (temporarily)
	 * idle.
	 * 
	 * @param working
	 *            True if the tailer is performing work, false to indicate work
	 *            has stopped and the tailer is idle (for a while).
	 */
	protected void fireWork(final boolean working) {
		log.trace("Informing listeners of work status: " + working);
		final List<TailerListener> listeners = copyListenerList();
		executor.execute(new Runnable() {

			@Override
			public void run() {
				for (final TailerListener listener : listeners) {
					if (working) {
						listener.tailerWorking();
					} else {
						listener.tailerIdle();
					}
				}
			}
		});
	}

	/**
	 * Inform listeners that the tailer has stopped following (a file) and no
	 * more work will be done until the tailer is re-started.
	 */
	protected void fireStopped() {
		log.trace("Informing listeners that tailer has stopped");
		final List<TailerListener> listeners = copyListenerList();
		executor.execute(new Runnable() {

			@Override
			public void run() {
				for (final TailerListener listener : listeners) {
					listener.tailerStopped();
				}
			}
		});
	}

	/**
	 * Informs listeners that the tailer has been reset.
	 * 
	 * @param lines
	 *            The new log blocks, may not be null.
	 */
	protected void fireReset() {
		log.trace("Informing listeners that tailer has been reset");
		final List<TailerListener> listeners = copyListenerList();
		executor.execute(new Runnable() {

			@Override
			public void run() {
				for (final TailerListener listener : listeners) {
					listener.tailerHasBeenReset();
				}
			}
		});
	}
}
